//
//  GlobalHelper.swift
//  ZYA
//
//  Created by Ken on 2019/8/1.
//  Copyright © 2019 zwb. All rights reserved.
//

import UIKit
import Moya
import SAMKeychain
import CoreLocation
import AdSupport
import SwiftyJSON

// 防止方法短时间重复调用控制
// interval 控制时间 action 执行方法
//func someMethod() {
//    print("Some method is called.")
//}
//
//let limitedSomeMethod = rateLimited(interval: 1, action: someMethod)
//limitedSomeMethod()
//limitedSomeMethod()
func rateLimited(interval: TimeInterval, action: @escaping () -> Void) -> () -> Void {
    var lastCallTime: TimeInterval = 0
    return {
        let currentTime = Date().timeIntervalSince1970
        if currentTime - lastCallTime >= interval {
            action()
            lastCallTime = currentTime
        } else {
            print("Method call is limited within \(interval) seconds.")
        }
    }
}






public func mirror(_ reflecting: Any) {
    let hMirror = Mirror(reflecting: reflecting)
    KSLog("对象类型:\(hMirror.subjectType)")
    KSLog("对象子元素个数:\(hMirror.children.count)")
    KSLog("--- 👇👇👇对象子元素属性名和属性值👇👇👇 ---")
    for child in hMirror.children {
        KSLog("属性:\(child.label.unwrap) 👉👉👉 值:\(child.value)")
    }
}




/// 拼上这个web计算高度才能准，因为内容根据手机适配了

public func adaptHtml(_ str:String) -> String {
    return "<html><head><meta content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0\" name=\"viewport\"><style type=\"text/css\">img{display: inline-block;max-width: 100%}</style></head><body>\(str)</body></html>"
}

// MARK: -  图片压缩方法
public func resetImgSize(sourceImage : UIImage,maxImageLenght : CGFloat,maxSizeKB : CGFloat) -> Data {
    
    var maxSize = maxSizeKB
    
    var maxImageSize = maxImageLenght
    
    if (maxSize <= 0.0) {
        
        maxSize = 1024.0;
        
    }
    
    if (maxImageSize <= 0.0)  {
        
        maxImageSize = 1024.0;
        
    }
    
    //先调整分辨率
    
    var newSize = CGSize.init(width: sourceImage.size.width, height: sourceImage.size.height)
    
    let tempHeight = newSize.height / maxImageSize;
    
    let tempWidth = newSize.width / maxImageSize;
    
    if (tempWidth > 1.0 && tempWidth > tempHeight) {
        
        newSize = CGSize.init(width: sourceImage.size.width / tempWidth, height: sourceImage.size.height / tempWidth)
        
    }
    
    else if (tempHeight > 1.0 && tempWidth < tempHeight){
        
        newSize = CGSize.init(width: sourceImage.size.width / tempHeight, height: sourceImage.size.height / tempHeight)
        
    }
    
    UIGraphicsBeginImageContext(newSize)
    
    sourceImage.draw(in: CGRect.init(x: 0, y: 0, width: newSize.width, height: newSize.height))
    
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    
    UIGraphicsEndImageContext()
    
    var imageData = newImage?.jpegData(compressionQuality: 1.0)
    
    var sizeOriginKB : CGFloat = CGFloat((imageData?.count)!) / 1024.0;
    
    //调整大小
    
    var resizeRate = 0.9;
    
    while (sizeOriginKB > maxSize && resizeRate > 0.1) {
        
        imageData = newImage!.jpegData(compressionQuality: CGFloat(resizeRate))
        
        sizeOriginKB = CGFloat((imageData?.count)!) / 1024.0;
        
        resizeRate -= 0.1;
        
    }
    
    return imageData!
    
}
//单位税号(单位税号为15位、18位、20位数字和大写英文字母)：
public let taxno_pattern = "^[A-Z0-9]{15}$|^[A-Z0-9]{18}$|^[A-Z0-9]{20}$"

// 15为身份证正则表达式
public let idcard_pattern15 = "^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}$"
// 18位身份证正则表达式
public let idcard_pattern18 = "^[1-9]\\d{5}(18|19|([23]\\d))\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$"

// 电话号码正则表达式  1开头的11位数字
public let mobile_pattern11 = "^\\d{11}$"

public let email_pattern = "^[A-Za-z0-9\\u4e00-\\u9fa5]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$"

/// 正则匹配
///
/// - Parameters:
///   - regex: 匹配规则
///   - validateString: 匹配对test象
/// - Returns: 返回结果
func RegularExpression (regex:String,validateString:String) -> Bool {
    
    
    /// 正则规则
    let regex = try? NSRegularExpression(pattern: regex, options: [])
    /// 进行正则匹配
    if let results = regex?.matches(in: validateString, options: [], range: NSRange(location: 0, length: validateString.count)), results.count != 0 {
        return true
    } else {
        return false
    }
    
    
}

//校验身份证
func valiIDCard(idcard:String?) -> Bool {
    let whiteSpace = CharacterSet.whitespaces
    let manIdcard = idcard.unwrap.trimmingCharacters(in: whiteSpace) // 有可能服务端返回空格是坑
    if  manIdcard.count > 0  {
        
        if manIdcard.trimmingCharacters(in: whiteSpace).count == 15 {
            guard RegularExpression(regex: idcard_pattern15, validateString: manIdcard) else {
                return false
            }
            return true
        } else if  manIdcard.trimmingCharacters(in: whiteSpace).count == 18 {
            guard RegularExpression(regex: idcard_pattern18, validateString: manIdcard) else {
                return false
            }
            return true
        }  else {
            return false
        }
        
        
    } else {
        return true  // 没值不校验，返回合法
    }
}

//
//func dateOfNowStr() -> String {
//    let formatter = DateFormatter()
//    formatter.dateFormat = "yyyyMMddHHmmss"
//    return formatter.string(from: Date())
//}
// 递归打印view的子类
public func printSubViews(view:UIView,level:Int) {
    let subs = view.subviews
    guard subs.count > 0 else {
        return
    }
    for view_s in subs {
        var blank = " "
        for _ in 0..<level {
            blank += " "
        }
        
        print("\(blank)\(level)-",type(of:view_s))
        
        printSubViews(view: view_s, level: level + 1)
    }
}
// 非递归打印view的子类
public func printSubViews_x(view:UIView) {
    let subs = view.subviews
    var stack = StackG<UIView>()
    for item in subs {
        stack.push(item:item)
    }
    
    while stack.count > 0  {
        print( type(of:stack.peek()))
        if let subx = stack.pop()?.subviews,subx.count > 0 {
            subx.forEach {
                stack.pushBottom(item: $0)
            }
        }
        
        
    }
    
}

func containsDuplicate(_ nums: [Int]) -> Bool {
    return Set(nums).count == nums.count
}

// 实现了栈
struct StackG<Element> {
    
    private var items = [Element]()
    mutating func push(item:Element) {
        items.append(item)
    }
    mutating func pushBottom(item:Element) {
        items.insert(item, at: 0)
    }
    
    mutating func pop() ->Element? {
        return items.removeLast()
    }
    func empty() -> Bool {
        return items.isEmpty
    }
    var count:Int {
        return items.count
    }
    func peek() ->Element? {
        return items.last
    }
}


func timeToStam(_ time: Date) -> Int {
    
    let dformatter = DateFormatter()
    dformatter.dateFormat = "yyyy年MM月dd日 HH:mm:ss"
    //当前时间的时间戳
    let timeInterval: TimeInterval = time.timeIntervalSince1970
    let timeStamp = Int(timeInterval)
    return timeStamp
}

func stampToTime (_ stamp:String) -> String {
    
    //转换为时间
    if let timeInterval = TimeInterval(stamp) {
        let date = NSDate(timeIntervalSince1970: timeInterval)
        
        //格式话输出
        let dformatter = DateFormatter()
        dformatter.dateFormat = "yyyy年MM月dd日 HH:mm:ss"
        let finalStr = dformatter.string(from: date as Date)
        return finalStr
    } else {
        return "时间转换出错"
    }
    
    
    
}

@discardableResult
public func alertUtil(_ observer:UIViewController? = nil,title:String,message:String =  "",aStyle:UIAlertController.Style = .alert,textFContent:String? = nil,textFPlaceholer:String? = nil,withCancel:Bool = true,operateS:[nameAction]) -> UIAlertController {
    
    let alertSheet = UIAlertController(title: title, message: message, preferredStyle: aStyle)
    if withCancel {
        let cancelAction = UIAlertAction(title: "取消", style:  .cancel, handler: nil)
        alertSheet.addAction(cancelAction)
    }
    if textFContent.isSome || textFPlaceholer.isSome {
        alertSheet.addTextField { textF in
            textF.text = textFContent.unwrap
            textF.placeholder = textFPlaceholer.unwrap
        }
    }
    operateS.forEach {
        let act = UIAlertAction(title: $0.name, style: .default, handler: $0.action)
        alertSheet.addAction(act)
    }
    
    DispatchQueue.main.async {
        if observer.isNone {
            let topViewController = RouterUtils.currentTopViewController()!
            topViewController.present(alertSheet, animated: true, completion: nil)
        } else {
            observer!.present(alertSheet, animated: true, completion: nil)
        }
    }
    return alertSheet
}

public struct nameAction {
    var name:String
    var action:((UIAlertAction) -> ())
}

struct RuntimeKeys {
    static var selectedKey: String = "selectedKey"
    static var inputStrKey: String = "inputStrKey"
    static var refreshKey: String = "refreshKey"
    static var foldKey: String = "foldKey"
    
    static var excuteKey: String = "excuteKey"
    static var hfMoneyKey: String = "hfMoneyKey"
    static var configurationKey: String = "configurationKey"
    static var filterPaKey: String = "filterPaKey"
    static var tenderfilterPaKey: String = "tenderfilterPaKey"
    static var typeDicDescKey: String = "typeDicDescKey"
    static var dropMenuKey: String = "dropMenuKey"
    static var taskidKey: String = "taskidKey"
    static var auditPersonIndexKey: String = "auditPersonIndexKey"
    
    static var handsignimgKey: String = "handsignimgKey"

    static var startDateKey: String = "startDateKey"
    static var endDateKey: String = "endDateKey"
    static var barbuttonitemKey: String = "barbuttonitemKey"
    static var startCnDtKey: String = "startCnDtKey"
    static var endCnDtKey: String = "endCnDtKey"
    static var auditNumKey:String = "auditNumKey"
    static var auditNumLabelKey:String = "auditNumLabelKey"
    static var showCountKey = "showCountKey"
    static var notice_datakey = "notice_datakey"
}


protocol operatable: AnyObject {
    var excuteType:OperateType? {get set}
}
extension operatable {
    var excuteType: OperateType? {
        
        set {
            objc_setAssociatedObject(self, &RuntimeKeys.excuteKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_COPY_NONATOMIC)
        }
        
        get {
            return (objc_getAssociatedObject(self, &RuntimeKeys.excuteKey) as? OperateType) ?? OperateType.add
        }
        
        
    }
}
enum OperateType {
    //修改
    // 新增
    // 查看
    //审核
    // 复制（大部分复制某单据，个别数据需要清空，实际是个新增操作）
    //确认（对某个单据确认，全部不可编辑）
    // 补录（猪只死亡特有的类型，补录照片）
    // 扫码新增（扫码新增主要限制个体号不可修改）
    case fix, add, view , audit, copy, confirm,  supplement,scanAdd,auditing,audited
}

protocol refreshable: AnyObject {
    var refreshClosure: (() -> Void)? { get set }
}
extension refreshable {
    var refreshClosure: (() -> Void)? {
        
        set {
            objc_setAssociatedObject(self, &RuntimeKeys.refreshKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_COPY_NONATOMIC)
        }
        
        get {
            return (objc_getAssociatedObject(self, &RuntimeKeys.refreshKey) as? () -> Void)
        }
        
        
    }
}

func strArr2MultArr(_ strDic:[String:String]) -> [MultipartFormData] {
    
    var multArr = [MultipartFormData]()
    for item in strDic {
        let data:Data = item.value.data(using: String.Encoding.utf8)!
        let multOne = MultipartFormData(provider: .data(data), name: item.key)
        multArr.append(multOne)
    }
    return multArr
}

// 字典转 json  旧版还是得留着，替换工作量太大了,备着
func dictionaryToJson_noprint(_ dictionary: Any) -> String? {
    do {
        let data = try JSONSerialization.data(withJSONObject: dictionary, options: [])
        return String(data: data, encoding: String.Encoding.utf8)!
    } catch {
        return nil
        
    }
}

func dictionaryToJsonBack(_ dictionary: Any) -> String? {
    do {
        let data = try JSONSerialization.data(withJSONObject: dictionary, options: JSONSerialization.WritingOptions.prettyPrinted)
        return String(data: data, encoding: String.Encoding.utf8)!
    } catch {
        return nil
        
    }
}

func dictionaryToJson(_ dict:[String:Any]) -> String {
    var result:String = ""
    do {
        //如果设置options为JSONSerialization.WritingOptions.prettyPrinted，则打印格式更好阅读
        let jsonData = try JSONSerialization.data(withJSONObject: dict, options: JSONSerialization.WritingOptions.prettyPrinted)
        
        if let JSONString = String(data: jsonData, encoding: String.Encoding.utf8) {
            result = JSONString
        }
        
    } catch {
        result = ""
    }
    return result
}



// JSONString转换为字典

func dictionaryFromJSON(jsonString:String) -> NSDictionary {
    
    let jsonData:Data = jsonString.data(using: .utf8)!
    let dict = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
    if dict != nil {
        return dict as! NSDictionary
    }
    return NSDictionary()
}

/// string  类型安全转为 double
func floatTran(_ a: String?) -> Float {
    guard let dv = Float(a.standby("0")) else {
        return 0
    }
    return dv
}
/// string  类型安全转为 double
func doubleTran(_ a: String?) -> Double {
    guard let dv = Double(a.standby("0")) else {
        return 0
    }
    return dv
}
/// string  类型安全转为 int
func intTran(_ a: String?) -> Int {
    guard let dv = Int(a.standby("0")) else {
        return 0
    }
    return dv
}

struct SmModel {
    var value:Optional<Any>?
    var msg:String?
    var type:ValidationType?
    var expresionBool:Bool?
    var stop = true
    init(value:Optional<Any>?,msg:String?,stop:Bool = true) {
        self.value = value
        self.msg = msg
        self.type = .required
    }
    init(expresionBool:Bool?,msg:String?,type:ValidationType?,stop:Bool = true) {
        self.msg = msg
        self.type = type
        self.expresionBool = expresionBool
    }
}
enum ValidationType {
    case required,expression  // 可选型 不为空类型，表达式类型
}
// 非空校验工具
class Validation {
    
    static let shared = Validation()
    
    var valiArr = [SmModel]()
    var valiArrAsync = [SmModel]()
    /// 非空的校验, 支持string  double 等常用类型
    @discardableResult
    func required(value:Optional<Any>?,msg:String) -> Validation {
        valiArr.append(SmModel(value: value, msg: msg,stop: true))
        return self
    }
    /// 传入表达式校验  stop 是否阻止，默认是   ，否的话提示且放过
    @discardableResult
    func expression(_ expresionBool:Bool,msg:String,stop:Bool = true) -> Validation {
        if stop {
            valiArr.append(SmModel( expresionBool: expresionBool,msg: msg, type:.expression,stop: stop))
        } else {
            valiArrAsync.append(SmModel( expresionBool: expresionBool,msg: msg, type:.expression,stop: stop))
        }
        
        return self
    }
    // 同步校验  遇到则止
    func validate() -> Bool {
        for item in valiArr {
            if item.type == .required {
                guard let v = item.value, v.unwrap.count > 0 else {
                    KSToast(item.msg.unwrap)
                    valiArr.removeAll()
                    return false
                }
            } else {
                guard item.expresionBool! else {
                    KSToast(item.msg.unwrap)
                    valiArr.removeAll()
                    return false
                }
            }
            
        }
        valiArr.removeAll()
        return true
    }
    // 全校验，提示全部问题
    func validateCollect() -> Bool {
        
        var collectFlag = true
        var collectMsg = ""
        
        for item in valiArr {
            if item.type == .required {
                if let v = item.value, v.unwrap.count > 0 {
                    
                } else {
                    collectMsg += item.msg.unwrap + "\n"
                     collectFlag = false
                }
            } else {
                if item.expresionBool! {
                    
                } else {
                    collectMsg += item.msg.unwrap + "\n"
                     collectFlag = false
                }
            }
         }
        if collectMsg != "" {
            KSToast(collectMsg)
        }
        
        valiArr.removeAll()
        return collectFlag
    }
    // 异步校验，带非阻止提示的校验必须使用 异步校验  单个提示单个确认
    func validatexAsync(clo:@escaping((Bool)->()))  {
        startVali()
        
        func startVali() {
            if valiArrAsync.count > 0 {
                let item  = valiArrAsync.first!
                if item.expresionBool! {
                    valiArrAsync.remove(at: 0)
                    startVali()
                } else {
                    
                    let topViewController = RouterUtils.currentTopViewController()
                    alertUtil(topViewController!, title: "提示",message: item.msg.unwrap,withCancel: false, operateS: [nameAction(name: "不", action: { _ in
                        self.valiArrAsync.removeAll()
                        clo(false)
                    }),nameAction(name: "确定", action: { _ in
                        self.valiArrAsync.remove(at: 0)
                        startVali()
                    })])
                }
            } else {
                clo(true)
            }
            
        }
    }
    // 异步校验，带非阻止提示的校验必须使用 异步校验 统一提示
    func validatexAsyncColle(clo:@escaping((Bool)->()))  {
        startVali()
        
        func startVali() {
            if valiArrAsync.count > 0 {
                
                var alertMsg = ""
                for item in valiArrAsync {
                    if !item.expresionBool! {
                         alertMsg += "\(item.msg.unwrap)\n"
                    }
                }
                if alertMsg != "" {
                 
                     let topViewController = RouterUtils.currentTopViewController()
                    alertUtil(topViewController!, title: "提示",message: alertMsg,withCancel: false, operateS: [nameAction(name: "留在本页", action: { _ in
                        self.valiArrAsync.removeAll()
                        clo(false)
                    }),nameAction(name: "继续保存", action: { _ in
                        self.valiArrAsync.removeAll()
                        clo(true)
                    })])
                    
                    
                } else {
                    self.valiArrAsync.removeAll()
                    clo(true)
                }
        
                
            } else {
                clo(true)
            }
            
        }
    }
}



extension UIViewController {
    func present_FullScreen(to:UIViewController) {
        to.modalPresentationStyle = .fullScreen
        self.present(to, animated: true, completion: nil)
    }
}

/// 模型转字典时，没值时赋空值
func valueOrEmpty(value:Any?) -> String {
    return value.isSome ? "\(value!)" : ""
}


extension Dictionary where Value : Hashable {
    /// 字典交换键值
    func swapKeyValues() -> [Value : Key] {
        assert(Set(self.values).count == self.keys.count, "Values must be unique")
        var newDict = [Value : Key]()
        for (key, value) in self {
            newDict[value] = key
        }
        return newDict
    }
}
///  为class 实现深拷贝  
protocol Copyable: AnyObject, Codable {
    func copy() -> Self
}

extension Copyable {
    
    func copy() -> Self {
        let encoder = JSONEncoder()
        guard let data = try? encoder.encode(self) else {
            fatalError("encode失败")
        }
        let decoder = JSONDecoder()
        guard let target = try? decoder.decode(Self.self, from: data) else {
            fatalError("decode失败")
        }
        return target
    }
}
struct idValue {
    var id = ""
    var value = ""
}


///读取本地的文件
///
/// - Parameters:
///   - fileNameStr: 文件名称
///   - type: 文件类型
/// - Returns: 文件的数据
func readLocalData(fileNameStr:String,type:String) -> Any? {
    
    //读取本地的文件
    let path = Bundle.main.path(forResource: fileNameStr, ofType: type);
    let url = URL(fileURLWithPath: path!)
    // 带throws的方法需要抛异常
    do {
        /*
         * try 和 try! 的区别
         * try 发生异常会跳到catch代码中
         * try! 发生异常程序会直接crash
         */
        let data = try Data(contentsOf: url)
        let jsonData:Any = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers)
        return jsonData
        
    } catch let error as Error? {
        return error?.localizedDescription
    }
}

func getArrayFromJSONString(jsonString:String) ->NSArray {
    
    let jsonData:Data = jsonString.data(using: .utf8)!
    
    let array = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
    if array != nil {
        return array as! NSArray
    }
    return array as! NSArray
    
}

// JSONString转换为字典

func getDictionaryFromJSONString(jsonString:String) ->NSDictionary{
    
    let jsonData:Data = jsonString.data(using: .utf8)!
    
    let dict = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
    if dict != nil {
        return dict as! NSDictionary
    }
    return NSDictionary()
    
    
}

//MARK: - 获取IP地址
public func GetIPAddresses() -> String {
    var addresses = [String]()
    
    var ifaddr : UnsafeMutablePointer<ifaddrs>? = nil
    if getifaddrs(&ifaddr) == 0 {
        var ptr = ifaddr
        while (ptr != nil) {
            let flags = Int32(ptr!.pointee.ifa_flags)
            var addr = ptr!.pointee.ifa_addr.pointee
            if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
                if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {
                    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                    if (getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),nil, socklen_t(0), NI_NUMERICHOST) == 0) {
                        if let address = String(validatingUTF8:hostname) {
                            addresses.append(address)
                        }
                    }
                }
            }
            ptr = ptr!.pointee.ifa_next
        }
        freeifaddrs(ifaddr)
    }
    if addresses.count > 0 {
        return addresses.first!
    } else {
        return "0.0.0.0"
    }
    
}

/// 没字符的时候也可以点击搜索
class TenderSearchBar: UISearchBar {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        if #available(iOS 13, *) {
            
            self.searchTextField.enablesReturnKeyAutomatically = false
            
            if #available(iOS 26, *) {
                for subV in searchTextField.subviews {
                    if "\(subV.classForCoder)".rangeOf(str: "UISearchBarSearchFieldBackgroundView").length > 0 {
                        subV.removeFromSuperview()
                    }
                }
            }
            
        } else {
            
            if let searchTF: UITextField = self.value(forKey: "_searchField") as? UITextField {
                searchTF.enablesReturnKeyAutomatically = false
            }
            
        }
        
        
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        //fatalError("init(coder:) has not been implemented")
    }
}


public class RouterUtils {
  //获取当前页面
  public class func currentTopViewController() -> UIViewController? {
      
      if let kwindow =  UIApplication.shared.windows.filter({$0.isKeyWindow}).first,
        let rootViewController = kwindow.rootViewController {
 
      return currentTopVC(rootViewController: rootViewController)
    }else{
      return nil
    }
  }
  
  public class func currentTopVC(rootViewController: UIViewController) -> UIViewController {
    if (rootViewController.isKind(of: UITabBarController.self)) {
      let tabBarController = rootViewController as! UITabBarController
        
        var vc:UIViewController!
        if tabBarController.selectedViewController.isSome {
            vc = tabBarController.selectedViewController!
        } else if tabBarController.viewControllers.isSome, tabBarController.viewControllers!.count > 0 {
            vc = tabBarController.viewControllers!.first!
        }
        
        return currentTopVC(rootViewController: vc)
    }
    if (rootViewController.isKind(of: UINavigationController.self)) {
      let navigationController = rootViewController as! UINavigationController
      return currentTopVC(rootViewController: navigationController.visibleViewController!)
    }
    if ((rootViewController.presentedViewController) != nil) {
      return currentTopVC(rootViewController: rootViewController.presentedViewController!)
    }
    return rootViewController
  }
}

import AppTrackingTransparency
// 获取设备id ,并存入keychain 能保证卸载重装这个id不会变
func go2Setting() {
    let alert = UIAlertController(title: "签到等功能需要绑定设备权限", message: "请进入系统【设置】允许App请求跟踪，才能实现签到等功能", preferredStyle: .alert)
    
    let gotoAction = UIAlertAction(title: "前往设置", style: .default) { (_) in
        // 开启权限
        let url = URL(string: UIApplication.openSettingsURLString)
        if let anUrl = url {
            if UIApplication.shared.canOpenURL(anUrl) {
                if let anUrl = url {
                    UIApplication.shared.open(anUrl, options: [:], completionHandler: { result in
                    })
                }
            }
        }
    }
    
    let cancel = UIAlertAction(title: "取消", style: .cancel, handler: nil)
    alert.addAction(gotoAction)
    alert.addAction(cancel)
    if let topViewController = RouterUtils.currentTopViewController() {
        topViewController.present(alert, animated: true, completion: nil)
    }
    
}
// 申请一次跟踪 随缘是否同意 主要是审核需要马上看到这个申请
func requestTracking() {
    let redekey = "requestDeciveIdentifier"   // 0 是系统为申请过  1 是系统申请过了
     if UserDefaults.standard.string(forKey: redekey) == "1" {
        
    } else {
        if #available(iOS 14, *) {
            ATTrackingManager.requestTrackingAuthorization { status in
             
                // 随缘，申请过就好
                UserDefaults.standard.set("1", forKey: redekey)
                UserDefaults.standard.synchronize()
            }
        } else {
           
        }
      
    }
}
// 计算文本高度
public func computerHeight(_ text: String?, width: CGFloat, font: UIFont) -> CGFloat {
    guard let text = text else {
        return 0
    }
    let attribute = [NSAttributedString.Key.font: font]
    let option = NSStringDrawingOptions.usesLineFragmentOrigin
    let tempText = NSString(cString: text.cString(using: String.Encoding.utf8)!, encoding: String.Encoding.utf8.rawValue)
    let frame = tempText!.boundingRect(with: CGSize(width: width, height: 2000), options: option, attributes: attribute, context: nil)
    return frame.size.height
}
 

@discardableResult
// force  true 强制引导    false 没有就算了
func fetchDeciveIdentifier(force:Bool = false) -> String {
    let bundleId = Bundle.main.bundleIdentifier.unwrap
    var deviceId = SAMKeychain.password(forService: bundleId, account: "user").unwrap
    if deviceId != "" {
        return deviceId
    }
    deviceId = UIDevice.current.identifierForVendor?.uuidString ?? ""
    if deviceId != "" {
        SAMKeychain.setPassword(deviceId, forService: bundleId, account: "user")
         return deviceId
    }
 
    deviceId = ASIdentifierManager.shared().advertisingIdentifier.uuidString
    if deviceId != "" {
        SAMKeychain.setPassword(deviceId, forService: bundleId, account: "user")
         return deviceId
    }
    if force {
        go2Setting()
    }
    return deviceId
    
    
    
    
//    if UserDefaults.standard.string(forKey: redekey) == "1" {
//        return SAMKeychain.password(forService: bundleId, account: "user").unwrap
//    } else {
//        if #available(iOS 14, *) {
//            ATTrackingManager.requestTrackingAuthorization { status in
//                if status == .authorized {
//                    uuid = ASIdentifierManager.shared().advertisingIdentifier.uuidString
//                    SAMKeychain.setPassword(uuid, forService: bundleId, account: "user")
//                    UserDefaults.standard.set("1", forKey: redekey)
//                    UserDefaults.standard.synchronize()
//                } else {
//                    if force {
//                        go2Setting()
//                    }
//
//                }
//            }
//        } else {
//            if ASIdentifierManager.shared().isAdvertisingTrackingEnabled {
//                uuid = ASIdentifierManager.shared().advertisingIdentifier.uuidString
//                SAMKeychain.setPassword(uuid, forService: bundleId, account: "user")
//                UserDefaults.standard.set("1", forKey: redekey)
//                UserDefaults.standard.synchronize()
//            } else {
//                if force {
//                    go2Setting()
//                }
//            }
//        }
//        return ""
//    }
 
    
}

public let StatusBarHeight: CGFloat = UIViewController().ksStatusbarHeight

extension UIDevice{
    
    //判断设备是不是iPhoneX以及以上
    
    public func isiPhoneXorLater() -> Bool {
        
        let screenHieght = UIScreen.main.nativeBounds.size.height
        
        if screenHieght == 2436 || screenHieght == 1792 || screenHieght == 2688 || screenHieght == 1624 {
            return true
        }
        return false
    }
    
}




//刘海屏额外的头部高度
let heightOfAddtionalHeader:CGFloat = {
    if UIDevice.current.isiPhoneXorLater() {
        return 24.0
    } else {
        return 0.0
    }
}()
//刘海屏额外的头部高度
let TabbarHeight:CGFloat = {
    //Tabbar高度
    let topViewController = RouterUtils.currentTopViewController()
    return topViewController?.tabBarController?.tabBar.bounds.size.height ?? 0
}()
//刘海屏额外的底部高度
let heightOfAddtionalFooter = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0.0







func autoLabelHeight(with text:String , labelWidth: CGFloat ,attributes : [NSAttributedString.Key : Any]) -> CGFloat{
    var size = CGRect()
    let size2 = CGSize(width: labelWidth, height: 0)//设置label的最大宽度
    size = text.boundingRect(with: size2, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: attributes , context: nil);
    return size.size.height
}

// 版本
let VERSION = infoDic!["CFBundleShortVersionString"] as? String ?? ""

// 本地沙盒
var userDefault: UserDefaults = UserDefaults.standard
// infoPlist文件
var infoDic = Bundle.main.infoDictionary


// 经纬度距离计算公式
func getDistance(lat1:Double,lng1:Double,lat2:Double,lng2:Double) -> Double {
    let EARTH_RADIUS:Double = 6378137.0
    
    let radLat1:Double = radian(d: lat1)
    let radLat2:Double = radian(d: lat2)
    
    let radLng1:Double = radian(d: lng1)
    let radLng2:Double = radian(d: lng2)
    
    let a:Double = radLat1 - radLat2
    let b:Double = radLng1 - radLng2
    
    var s:Double = 2 * asin(sqrt(pow(sin(a/2), 2) + cos(radLat1) * cos(radLat2) * pow(sin(b/2), 2)))
    s = s * EARTH_RADIUS
    KSLog("经纬度距离计算\(s)")
    return s
}

//根据角度计算弧度
func radian(d:Double) -> Double {
    return d * Double.pi/180.0
}
//根据弧度计算角度
func angle(r:Double) -> Double {
    return r * 180/Double.pi
}
// 反射解包
func testXXX(_ obj: Any) -> Any {
    let mirror = Mirror(reflecting: obj)
    
    guard !mirror.children.isEmpty else {return obj}
    
    var keyValue: [String: Any] = [:]
    
    for children in mirror.children {
        if let keyName = children.label {
            keyValue[keyName] = testXXX(children.value)
        }else{
            print("children.label 为空")
        }
    }
    return keyValue
}




func fitDoubleString(json: JSON,keyx:String) -> String? {
    return json[keyx].double.isSome ? "\(json[keyx].double!)" : json[keyx].string
}
func fitIntString(json: JSON,_ keyx:String) -> String? {
    return json[keyx].int.isSome ? "\(json[keyx].int!)" : json[keyx].string
}
// MARK: - base64编码
public func base64Encoding(plainString:String) -> String {
    let plainData = plainString.data(using: String.Encoding.utf8, allowLossyConversion: true)
    let base64String = plainData?.base64EncodedString(options: NSData.Base64EncodingOptions.init(rawValue: 0))
    return base64String!
}
// MARK: - base64解码
public func base64Decoding(encodedString:String)->String {
    let decodedData = NSData(base64Encoded: encodedString, options: NSData.Base64DecodingOptions.init(rawValue: 0))
    let decodedString = NSString(data: decodedData! as Data, encoding: String.Encoding.utf8.rawValue)! as String
    return decodedString
}
public extension UIDevice {
    
    var modelName: String {
        var systemInfo = utsname()
        uname(&systemInfo)
        let machineMirror = Mirror(reflecting: systemInfo.machine)
        let identifier = machineMirror.children.reduce("") { identifier, element in
            guard let value = element.value as? Int8, value != 0 else { return identifier }
            return identifier + String(UnicodeScalar(UInt8(value)))
        }
        
        switch identifier {
 
  
        case "iPhone9,1", "iPhone9,3":  return "iPhone 7"
        case "iPhone9,2", "iPhone9,4":  return "iPhone 7 Plus"
        case "iPhone10,1", "iPhone10,4": return "iPhone 8"
        case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus"
        case "iPhone10,3", "iPhone10,6": return "iPhone X"
            
        case "iPhone11,8": return "iPhone XR"
        case "iPhone11,2": return "iPhone XS"
        case "iPhone11,4", "iPhone11,6": return "iPhone XS Max"
        case "iPhone12,1": return "iPhone 11"
        case "iPhone12,3": return "iPhone 11 Pro"
        case "iPhone12,5": return "iPhone 11 Pro Max"
            
            
        case "iPhone12,8":  return "iPhone SE2"
            
        case "iPhone13,1":  return "iPhone 12 mini"
            
        case "iPhone13,2":  return "iPhone 12"
            
        case "iPhone13,3": return  "iPhone 12 Pro"
            
        case "iPhone13,4":  return "iPhone 12 Pro Max"
            
     
        case "iPhone14,2":  return "iPhone 13 Pro"
            
        case "iPhone14,3":  return "iPhone 13 Pro Max"
        case "iPhone14,4":  return "iPhone 13 Mini"
        case "iPhone14,5":  return "iPhone 13"
        case "iPhone14,6":  return "iPhone SE 3rd Gen"
        case "iPhone14,7":  return "iPhone 14"
        case "iPhone14,8":  return "iPhone 14 Plus"
        case "iPhone15,2":  return "iPhone 14 Pro"
        case "iPhone15,3":  return "iPhone 14 Pro Max"
 
            
        case "iPad1,1": return "iPad"
        case "iPad1,2": return "iPad 3G"
        case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":  return "iPad 2"
        case "iPad2,5", "iPad2,6", "iPad2,7":  return "iPad Mini"
        case "iPad3,1", "iPad3,2", "iPad3,3":  return "iPad 3"
        case "iPad3,4", "iPad3,5", "iPad3,6":  return "iPad 4"
        case "iPad4,1", "iPad4,2", "iPad4,3":  return "iPad Air"
        case "iPad4,4", "iPad4,5", "iPad4,6":  return "iPad Mini 2"
        case "iPad4,7", "iPad4,8", "iPad4,9":  return "iPad Mini 3"
        case "iPad5,1", "iPad5,2":  return "iPad Mini 4"
        case "iPad5,3", "iPad5,4":  return "iPad Air 2"
        case "iPad6,3", "iPad6,4":  return "iPad Pro 9.7"
        case "iPad6,7", "iPad6,8":  return "iPad Pro 12.9"
            
        case "AppleTV2,1":  return "Apple TV 2"
        case "AppleTV3,1","AppleTV3,2":  return "Apple TV 3"
        case "AppleTV5,3":  return "Apple TV 4"
            
        case "i386", "x86_64":  return "Simulator"
            
        default:  return identifier
        }
    }
}
import UIKit
import Photos
class XXVideoCompression: NSObject {
    // 获取phasset 的data
    public func compressVideo(_ exportSession:AVAssetExportSession? , completion: @escaping (_ data: Data)-> Void) {
        
        let uuu = self.compressedUrl()
        exportSession?.outputURL = URL.init(fileURLWithPath: uuu)
        exportSession?.outputFileType = .mp4
        exportSession?.shouldOptimizeForNetworkUse = true;
        
        if let assetTime = exportSession?.asset.duration {
            let duration = CMTimeGetSeconds(assetTime)
            print("视频时长 \(duration)");
        }
        
        exportSession?.exportAsynchronously(completionHandler: {
            
            switch exportSession?.status{
                
            case .failed?:
                print("失败...\(String(describing: exportSession?.error?.localizedDescription))")
                completion(Data())
                break
            case .cancelled?:
                print("取消")
                completion(Data())
                break;
            case .completed?:
                print("转码成功")
                do {
                    let data = try Data.init(contentsOf: URL.init(fileURLWithPath: uuu), options: Data.ReadingOptions.init())
                    let mp4Path = URL.init(fileURLWithPath: uuu)
                    let size = self.fileSize(url: mp4Path)
                    print("视频时长\(size)")
                    completion(data)
                } catch let error {
                    print("失败 \(error)")
                    completion(Data())
                }
                
                break;
            default:
                print("..")
                completion(Data())
                break;
            }
        })
    }
    
    
    //保存压缩
    func compressedUrl() -> String {
        
        let string = NSHomeDirectory() + "/Documents/\(Date().timeIntervalSince1970).mp4"
        
        return string//URL.init(fileURLWithPath: string)
    }
    
    //计算视频大小
    func fileSize(url:URL) -> CGFloat {
        return CGFloat(NSData.init(contentsOf: url)?.length ?? 0 )/// 1024 / 1024
    }
    
    //获取视频首帧
    func imageWithVideoUrl(asset:PHAsset,completion: @escaping ( _ image:UIImage) -> Void) {
        
        let option = PHImageRequestOptions()
        option.isNetworkAccessAllowed = true
        option.resizeMode = .fast
        option.deliveryMode = .fastFormat
        PHImageManager.default().requestImage(for: asset,
                                              targetSize:PHImageManagerMaximumSize,
                                              contentMode: .aspectFill,
                                              options: option )
        { (image, info) in
            completion(image ?? UIImage())
        }
    }
}
func assetToData(asset:PHAsset,result:  @escaping ((Data?)->())) {
    let options = PHVideoRequestOptions()
    options.isNetworkAccessAllowed = true
    options.deliveryMode = .automatic
    
    PHImageManager.default().requestExportSession(forVideo: asset, options: options, exportPreset: AVAssetExportPresetMediumQuality) { (exportSession, info) in
        //将asset转换为AVAssetExportSession对象,用AVAssetExportSession转化为Data
        XXVideoCompression().compressVideo(exportSession, completion: { (data) in
            if data.count > 0 {//做判断,判断是否转化成功
                result(data)
            } else {
                result(nil)
            }
        })
    }
}
/// GCD定时器循环操作
///   - timeInterval: 循环间隔时间
///   - handler: 循环事件
@discardableResult
public func DispatchTimer(timeInterval: Double, handler:@escaping ()->()) -> DispatchSourceTimer
{
    let timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
    timer.schedule(deadline: .now() + timeInterval, repeating: timeInterval)
    timer.setEventHandler {
        DispatchQueue.main.async {
            KSLog("定时器执行")
            handler()
        }
    }
    timer.resume()
    return timer
}

// 捕获导航栏返回事件处理业务
protocol BackButtonDelegate {
    func navigationBackAction()
}

extension UINavigationController  {
    
    // 插入返回按钮的逻辑
    @objc func back() {
        let vc = self.topViewController
        if let v = vc as? BackButtonDelegate {
            v.navigationBackAction()
        } else {
            popViewController(animated: true)
        }
    }
}

public func getPicture(url:String) -> UIImage {
    
    var img = UIImage(named:"ks_error.jpg")!
    if let imageUrl = URL(string:  configURLOK(latter:url) ) {
         
        do {
            let data = try Data(contentsOf:imageUrl)
      
                if let image = UIImage(data: data) {
                    img = image
                }  else {
                    img = UIImage(named:"ks_error.jpg")!
                }
         } catch {
            // 捕获到错误，处理错误情况
            print("错误: \(error)")
        }
    
     }
    
    return img
}
public func any2Str(_ v:Any?) -> String {
    if let sv = v as? String {
        return sv
    }
    if let dv = v as? Double {
        return dv.cleanZero
    }
    return ""
}
// 根据属性名字符串获取属性值
func getValueByKey(obj:AnyObject, key: String) -> Any {
    let hMirror = Mirror(reflecting: obj)
    for case let (label?, value) in hMirror.children {
        if label == key {
            return value
        }
    }
    return NSNull()
}

// 取出某个对象的地址
func getAnyObjectMemoryAddress(object: AnyObject) -> String {
    let str = Unmanaged<AnyObject>.passUnretained(object).toOpaque()
    return String(describing: str)
}

// 对比两个对象的地址是否相同
func objectEquateable(object1: AnyObject, object2: AnyObject) -> Bool {
    let str1 = getAnyObjectMemoryAddress(object: object1)
    let str2 = getAnyObjectMemoryAddress(object: object2)
    
    if str1 == str2 {
        return true
    } else {
        return false
    }
}
import Foundation

enum ADerection {
    case top, bottom
}
private var _coverKey = "alertCoverKey"
private var _didRemoveKey = "didRemoveKey"

private var _constraintsClosureKey = "constraintsClosureKey"

protocol AlertSystemAnimationProtocol {
    /// 弹出
    func present()
    /// 消失
    func dismiss()
    /// alert 与 cover 的约束
    /// (注意调用顺序，应在 present 之前)
    func constrains(_ closure: @escaping (ConstraintMaker) -> Void)
}

extension AlertSystemAnimationProtocol where Self: UIView {
    
    private var _alertCover: UIView {
        set {
            objc_setAssociatedObject(self, &_coverKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        get {
            return ((objc_getAssociatedObject(self, &_coverKey) as? UIView) ?? UIView())
        }
    }
    private var didRemove: (()->())? {
        set {
            objc_setAssociatedObject(self, &_didRemoveKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        get {
            return ((objc_getAssociatedObject(self, &_didRemoveKey) as? (()->())))
        }
    }
    
    private var _constrainsClosure: ((ConstraintMaker) -> Void)? {
        set {
            objc_setAssociatedObject(self, &_constraintsClosureKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        get {
            guard let closure = objc_getAssociatedObject(self, &_constraintsClosureKey) as? ((ConstraintMaker) -> Void) else {
                return nil
            }
            return closure
        }
    }
    
    func constrains(_ closure: @escaping (ConstraintMaker) -> Void) {
        _constrainsClosure = closure
    }
    
    func present() {
        let cover = UIView()
        cover.frame = UIScreen.main.bounds
        cover.backgroundColor = UIColor(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 0.35)
        cover.addSubview(self)
        
        if let constraintsClosure = _constrainsClosure {
            self.snp.remakeConstraints(constraintsClosure)
        } else {
            self.snp.makeConstraints { make in
                make.center.equalToSuperview()
                make.width.equalTo(KSGlobal.screenW * 0.75)
                make.height.equalTo(KSGlobal.screenH * 0.5)
                
            }
        }
        
        cover.alpha = 0
        self.alpha = 0.5
        
        if let keyWindow = getKeyWindow() {
            keyWindow.addSubview(cover)
            cover.layoutIfNeeded()
        }
        
        self.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
        
        UIView.animate(withDuration: 0.1, delay: 0, options: .curveEaseInOut) {
            cover.alpha = 1
            self.alpha = 1
            self.transform = CGAffineTransform.identity
        }
        _alertCover = cover
    }
    
    // 镂空弹窗专用
    func presentS(_ loukong: CGRect, direction: ADerection, didRemove: (() -> ())? = nil) {
        
        var loukong = loukong
        
        self.didRemove = didRemove
        let bbColor = UIColor(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 0.35)
        let cover = UIView()
        cover.frame = UIScreen.main.bounds
        cover.backgroundColor = .clear
        let pianyi:CGFloat = 10
        
        let loukongC = UIView()
        loukongC.backgroundColor = .clear
        
        let topC = UIView()
        topC.backgroundColor = bbColor
        
        let leftC = UIView()
        leftC.backgroundColor = bbColor
        
        let rightC = UIView()
        rightC.backgroundColor = bbColor
        
        let bottomC = UIView()
        bottomC.backgroundColor = bbColor
        loukongC.frame = loukong
        
        /// 镂空部分是否是最右边的 item
        var isTheFarRight = false
        if PlatformManager.shared.platform == .zhuYouAn {
            if KS_SCREEN_WIDTH - loukongC.ks_maxX < loukong.width {
                isTheFarRight = true
                loukong.size = CGSize(width: loukong.width + 8, height: loukong.height)
                loukongC.frame = loukong
            }
        }
        
        if direction == .top {
            
            // 不知道为什么要  偏移10个距离，先注释
//            topC.frame = CGRect(x: 0, y: 0, width: KSGlobal.screenW, height: loukong.minY - pianyi)
//            leftC.frame = CGRect(x: 0, y: loukong.minY - pianyi, width: loukong.minX, height: loukong.height + pianyi)
//            rightC.frame = CGRect(x: loukong.maxX, y: loukong.minY - pianyi, width: KSGlobal.screenW - loukong.maxX, height: loukong.height + pianyi)
//            bottomC.frame = CGRect(x: 0, y: loukong.maxY, width: KSGlobal.screenW, height: KSGlobal.screenH - loukong.maxY)
            
            topC.frame = CGRect(x: 0, y: 0, width: KSGlobal.screenW, height: loukong.minY)
            leftC.frame = CGRect(x: 0, y: loukong.minY, width: loukong.minX, height: loukong.height)
            rightC.frame = CGRect(x: loukong.maxX, y: loukong.minY, width: KSGlobal.screenW - loukong.maxX, height: loukong.height)
            bottomC.frame = CGRect(x: 0, y: loukong.maxY, width: KSGlobal.screenW, height: KSGlobal.screenH - loukong.maxY)
            
 
        } else {
            topC.frame = CGRect(x: 0, y: 0, width: KSGlobal.screenW, height: loukong.minY)
            leftC.frame = CGRect(x: 0, y: loukong.minY, width: loukong.minX, height: loukong.height + pianyi)
            rightC.frame = CGRect(x: loukong.maxX, y: loukong.minY, width: KSGlobal.screenW - loukong.maxX, height: loukong.height + pianyi)
            bottomC.frame = CGRect(x: 0, y: loukong.maxY + pianyi, width: KSGlobal.screenW, height: KSGlobal.screenH - loukong.maxY)
        }
        
        
        cover.addSubview(loukongC)
        cover.addSubview(topC)
        cover.addSubview(leftC)
        cover.addSubview(rightC)
        cover.addSubview(bottomC)
        
        
     
        
        let cornerWW:CGFloat = 10
        // 给镂空添加圆角
        if direction == .top {
            // 用于遮挡莫名其妙的一根横线
            let whiteLine = UIView().extBgColor(color: .white)
            cover.addSubview(whiteLine)
            
            let rLT = CornerView(width: cornerWW, outColor: .white, innerColor: bbColor, type: .rightTop)
            rLT.frame = CGRect(x: loukong.minX - cornerWW, y: loukong.minY, width: cornerWW, height: cornerWW)
            cover.addSubview(rLT)
 
            let rRT = CornerView(width: cornerWW, outColor: .white, innerColor: bbColor, type: .leftTop)
            rRT.frame = CGRect(x: loukong.maxX, y: loukong.minY, width: cornerWW, height: cornerWW)
            
            if !isTheFarRight {
                cover.addSubview(rRT)
                
                whiteLine.frame = CGRect(x: rLT.ks_x, y: rLT.ks_y - 0.5, width: rRT.ks_maxX - rLT.ks_x , height: 1)
            } else {
                whiteLine.frame = CGRect(x: rLT.ks_x, y: rLT.ks_y - 0.5, width: loukong.maxX - rLT.ks_x , height: 1)
            }
            
 
            let rLB = CornerView(width: cornerWW, outColor:bbColor , innerColor: .white, type: .leftBottom)
            rLB.frame = CGRect(x: loukong.minX, y: loukong.maxY - cornerWW, width: cornerWW, height: cornerWW)
            cover.addSubview(rLB)
 
            let rRB = CornerView(width: cornerWW, outColor: bbColor, innerColor: .white, type: .rightBottom)
            rRB.frame = CGRect(x: loukong.maxX - cornerWW, y: loukong.maxY - cornerWW, width: cornerWW, height: cornerWW)
            cover.addSubview(rRB)
  
         } else {
             let rLT = CornerView(width: cornerWW, outColor: bbColor, innerColor: .white, type: .leftTop)
             rLT.frame = CGRect(x: loukong.minX, y: loukong.minY, width: cornerWW, height: cornerWW)
             cover.addSubview(rLT)
             
             let rRT = CornerView(width: cornerWW, outColor: bbColor, innerColor:.white , type: .rightTop)
             rRT.frame = CGRect(x: loukong.maxX - cornerWW, y: loukong.minY, width: cornerWW, height: cornerWW)
             cover.addSubview(rRT)
             
             let rLB = CornerView(width: cornerWW, outColor: .white , innerColor:bbColor , type: .rightBottom)
             rLB.frame = CGRect(x: loukong.minX - cornerWW, y: loukong.maxY - cornerWW + 0.5, width: cornerWW, height: cornerWW)
             cover.addSubview(rLB)
             
             let rRB = CornerView(width: cornerWW, outColor: .white, innerColor: bbColor, type: .leftBottom)
             rRB.frame = CGRect(x: loukong.maxX, y: loukong.maxY - cornerWW + 0.5, width: cornerWW, height: cornerWW)

             if !isTheFarRight {
                 // 不是最右边的 item
                 cover.addSubview(rRB)
             }
         }
        
        
        cover.addSubview(self)
        
        // 小三角添加到这个层级
        if PlatformManager.shared.platform != .zhuYouAn {
            let hasSubTriangle = UIImageView()
            hasSubTriangle.image = UIImage(named: "up_tri")
            cover.addSubview(hasSubTriangle)
            hasSubTriangle.frame = CGRect(x: loukong.maxX - 16, y: loukong.minY +  1, width: 10, height: 10)
        }
         
        
        let tapCover = UITapGestureRecognizer()
        tapCover.numberOfTapsRequired = 1
        tapCover.addTap { [unowned self] _ in
            self.dismiss()
        }
        loukongC.addGestureRecognizer(tapCover)
        
        let tap = UITapGestureRecognizer()
        tap.numberOfTapsRequired = 1
        tap.addTap { [unowned self] _ in
            self.dismiss()
        }
        topC.addGestureRecognizer(tap)
        
        
        
        let tap2 = UITapGestureRecognizer()
        tap2.numberOfTapsRequired = 1
        tap2.addTap { [unowned self] _ in
            self.dismiss()
        }
        leftC.addGestureRecognizer(tap2)
        
        let tap3 = UITapGestureRecognizer()
        tap3.numberOfTapsRequired = 1
        tap3.addTap { [unowned self] _ in
            self.dismiss()
        }
        rightC.addGestureRecognizer(tap3)
        let tap4 = UITapGestureRecognizer()
        tap4.numberOfTapsRequired = 1
        tap4.addTap { [unowned self] _ in
            self.dismiss()
        }
        bottomC.addGestureRecognizer(tap4)
        
        if let constraintsClosure = _constrainsClosure {
            self.snp.remakeConstraints(constraintsClosure)
        } else {
            self.snp.makeConstraints { make in
                make.center.equalToSuperview()
                make.width.height.equalTo(200)
            }
        }
        
        
        
        cover.alpha = 0
        self.alpha = 0.5
        
        getKeyWindow()?.addSubview(cover)
        cover.layoutIfNeeded()
        
        self.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
        
        UIView.animate(withDuration: 0.1, delay: 0, options: .curveEaseInOut) {
            cover.alpha = 1
            self.alpha = 1
            self.transform = CGAffineTransform.identity
        }
        _alertCover = cover
    }
    
    func dismiss() {
        UIView.animate(withDuration: 0.1, delay: 0, options: .curveEaseInOut) {
            self._alertCover.alpha = 0
        } completion: { isCompleted in
            if isCompleted {
                self.removeFromSuperview()
                self._alertCover.removeFromSuperview()
                if let clo = self.didRemove {
                    clo()
                }
            }
        }
    }
}
public struct nameBtnAction {
    var name:String
    var action:((UIButton) -> ())
}
 
func getKeyWindow() -> UIWindow? {
    
    if let window = UIApplication.shared.windows.filter({ $0.isKeyWindow }).first {
        return window
    }

    return UIApplication.shared.connectedScenes
        .filter { $0.activationState == .foregroundActive }
        .compactMap { $0 as? UIWindowScene }
        .first?.windows
        .first(where: { $0.isKeyWindow })
}

func getKeyWindow(after: ((UIWindow) -> Void)?) {
    if let keyWindow = getKeyWindow() {
        after?(keyWindow)
    }
}

func go2KSCalendar(contr: UIViewController, infoDic:[String:String]?, maxSelectDate: String? = nil, minSelectDate: String? = nil, selectedDate: ((String) -> ())?) {
    
    let cvc = KSCalendar()
    cvc.calendarViewSelectclosure = { date in
        if let clo = selectedDate {
            clo(date)
        }
    }
    if let max = maxSelectDate {
        cvc.maxSelectDate = max
    }
    if let min = minSelectDate {
        cvc.minSelectDate = min
    }
    if let inD = infoDic {
        cvc.subInfoDic = inD
    }
    cvc.isRadio = true
    
    let nav = BaseNavigationController()
    nav.viewControllers = [cvc]
    let currentVC = RouterUtils.currentTopViewController()!
    currentVC.present_FullScreen(to: nav)
}

func getTopH() -> CGFloat {
    var navH: CGFloat = 0
    if let currentVC = RouterUtils.currentTopViewController() {
       navH = currentVC.navigationController.isSome ? currentVC.navigationController!.navigationBar.frame.height : 0
    }
    let topH = UIViewController().ksStatusbarHeight + navH
    return topH
}
